package com.example.warehousems.service;

import com.example.warehousems.component.UserInfo;
import com.example.warehousems.dao.*;
import com.example.warehousems.dao.model.*;
import com.example.warehousems.event.publisher.InOutEventPublisher;
import com.example.warehousems.event.publisher.UserRequestPublisher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
public class WarehouseService {
    @Autowired
    private CargoRepository cargoRepository;
    @Autowired
    private BranchRepository branchRepository;
    @Autowired
    private StorageRepository storageRepository;
    @Autowired
    private StorageBufferRepository storageBufferRepository;
    @Autowired
    private OutListRepository outListRepository;
    @Autowired
    private InOutEventPublisher inOutEventPublisher;
    @Autowired
    private UserRequestPublisher userRequestPublisher;

    @Transactional
    public RESULT putCargoIntoStorage(Cargo cargo) {
        // cargo should have a max volume and weight
        if (cargo.getVolume() > 100 || cargo.getWeight() > 100) {
            return RESULT.CARGO_TOO_LARGE;
        }
        // check if the branch exists
        var branch = branchRepository.findBranchById(cargo.getOutBranch().getId());
        if (Objects.isNull(branch)) {
            return RESULT.WRONG_DESTINATION;
        }
        // find a position in storage
        var percentage = getStoragePercent();
        if (percentage > 0.9) {
            // put cargo into storage buffer
            var used = storageBufferRepository.getUsedStorageBufferCount();
            var total = storageBufferRepository.getStorageBufferCount();
            if (used == total) {
                return RESULT.BUFFER_FULL;
            }
            storageBufferRepository.save(new StorageBuffer());
            return RESULT.IN_BUFFER;
        }
        // put cargo into storage
        var storage = arrangeCargoInStorage(cargo);
        storage.setCargo(cargo);
        storageRepository.save(storage);
        cargoRepository.save(cargo);

        inOutEventPublisher.publish(storage, 1);
        userRequestPublisher.publish(storage, 1);
        return RESULT.SUCCESS;
    }

    @Transactional
    public Storage arrangeCargoInStorage(Cargo cargo) {
        var storage = storageRepository.findFirstByStatus(0);
        log.info(String.valueOf(storage));
        cargo.setStatus(3); //表示入库中
        storage.setStatus(2); //表示清理中，实质是中间态
        return storage;
    }

    public List<OutList> showOutList() {
        List<OutList> list = new ArrayList<>();
        // 一个子List来自同一个Branch
        var branchList = branchRepository.findAll();
        for (var branch : branchList) {
            List<OutList> outLists = outListRepository.findByBranchAndIs_delete(branch, false);
            list.addAll(outLists);
        }
        return list;
    }

    @Transactional
    public void moveCargoOut(Integer id) {
        // id为outList一条记录的id
        outListRepository.setIsDeleteTrue(id);

        OutList outList = outListRepository.findOutListById(id);
        List<Cargo> cargoList = outList.getCargoList();
        for (Cargo c : cargoList) {
            Integer cargoId = c.getId();

            Storage storage = storageRepository.findByCargoId(cargoId);
            storage.setStatus(2); // 表示清理中
            storageRepository.save(storage);

            c.setStatus(4); // 表示出库中
            cargoRepository.save(c);

            inOutEventPublisher.publish(storage, 2);
            userRequestPublisher.publish(storage, 2);
        }
    }

    @Transactional
    public void inDone(Storage s) {
        Cargo c = s.getCargo();
        System.out.println(c);
        storageRepository.save(s);

        List<OutList> curBranchList = outListRepository.findByBranchAndIsDeleteAndIsAvailable(c.getOutBranch(), false, true);
        if (curBranchList.size() == 0 || !isAbleToAppend(c, curBranchList)) { // 这里的判断条件还有一个||体积重量
            System.out.println("new OutList");
            OutList outList = new OutList(LocalDateTime.now(), false, true);

            outList.setBranch(c.getOutBranch());
            outList = outListRepository.save(outList); // 保存新的OutList对象到数据库，并更新outList变量
            c.setOutList(outList);
            cargoRepository.save(c);

            List<Cargo> cargoList = new ArrayList<>();
            cargoList.add(c);
            outList.setCargoList(cargoList);
            outListRepository.save(outList); // 将更新后的OutList保存到数据库中
            System.out.println(outList);
        } else { // 直接添加
            System.out.println("append");
            System.out.println(curBranchList);
            var outList = curBranchList.get(0);
            c.setOutList(outList);
            cargoRepository.save(c);
            List<Cargo> cargoList = outList.getCargoList();
            cargoList.add(c);
            outList.setCargoList(cargoList);
            outListRepository.save(outList);
            System.out.println(outList);
        }
    }

    private boolean isAbleToAppend(Cargo c, List<OutList> curBranchList) {
        if (curBranchList.size() == 1) {
            var outList = curBranchList.get(0);
            List<Cargo> cargoList = outList.getCargoList();
            Float maxW = 2000.0f, maxV = 30.0f, curW = 0.0f, curV = 0.0f;
            for (Cargo cargo : cargoList) {
                curW += cargo.getWeight();
                curV += cargo.getLength() * cargo.getWidth() * cargo.getHeight();
            }
            if (curW + c.getWeight() > maxW || curV + c.getLength() * c.getWidth() * c.getHeight() > maxV) {
                // 简化逻辑
                outList.setIs_available(false);
                outListRepository.save(outList);
                return false;
            }
            return true;
        }
        System.out.println("当前outList不止一个，系统错误");
        return false;
    }

    @Transactional
    public void outDone(Storage s) {
        Cargo c = s.getCargo();
        c.setOutTime(LocalDateTime.now());
        System.out.println(c);
        storageRepository.save(s);
        cargoRepository.save(c);
    }


    // get percentage of current use info
    public float getStoragePercent() {
        var usedCount = storageRepository.getUsedStorageCount();
        var total = storageRepository.getStorageCount();
        return usedCount / total;
    }

    // clear old data, init new storage data
    public void initStorage() {
        storageRepository.deleteAll();
        var shelfList = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
        var floorList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        var numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        for (var shelf : shelfList) {
            var list = new ArrayList<Storage>();
            for (var floor : floorList) {
                for (var number : numberList) {
                    list.add(new Storage(shelf, floor, number, null, 0));
                }
            }
            storageRepository.saveAll(list);
        }
    }

    public List<List> listStorage(String shelf) {
        var role = UserInfo.get("role");
        if (!Objects.equals(role, "admin"))
            return null; // TODO:逻辑有待修改

        var res = new ArrayList<List>();
        var floorList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        for (var floor : floorList) {
            var sameFloorList = storageRepository.findNumberByShelfAndFloor(shelf, floor);
            res.add(sameFloorList);
        }
        return res;
    }

    public Storage getStorage(String shelf, Integer floor, Integer number) {
        return storageRepository.findById(new StorageCoKey(shelf, floor, number)).orElse(null);
    }

    public Storage getStorageByCargoId(Integer cargoId) {
        return storageRepository.findByCargoId(cargoId);
    }

    public enum RESULT {
        SUCCESS,
        IN_BUFFER,
        BUFFER_FULL,
        WRONG_DESTINATION,
        CARGO_TOO_LARGE,
        WRONG_OPERATION
    }
}
